"""
========
m.common
========

A library currently only containing the common nodes.

"""

from m.core import ArgumentParser, Conditional, Node, NodeList, Tag
from m.exceptions import NoSuchSubtag

class TextNode(Node):
    """
    TextNode
    ========

    The most basic node, containing only plain text.
    
    """
    def __init__(self, value):
        """
        TextNode.__init__
        =================

        __init__(self, value) -> TextNode instance
        Instantiate the node and store the sent data as plain text.
        
        """
        self.value = value
    
    def render(self, context):
        """
        TextNode.render
        ===============

        render(self, context) -> string
        Return a basic string containing the nodes value.
        
        """
        return self.value

class PrintNode(Node):
    """
    PrintNode
    =========

    A node containing a dynamic value. The most common examples of usage follow:

    ::

        [% print "Hello, world!" %]    Outputs ``Hello, world!``
        [% print foo %]                Outputs the contents of the ``foo`` context variable.

    Note that print nodes currently only take one argument.
    
    """
    keyword = 'print'
    def __init__(self, value):
        """
        PrintNode.__init__
        ==================

        __init__(self, value) -> PrintNode instance
        Instantiate the node and store ``value`` (the argument to be parsed) as plain text.
        
        """
        self.value = value

    def render(self, context):
        """
        PrintNode.render
        ===================

        render(self, context) -> string
        Parse the previously stored argument inside the sent ``context``.
        
        """
        return str(ArgumentParser.parse_one(context, self.value))

class EndNode(Tag):
    """
    EndNode
    =======

    A node that merely exists for ending other tag nodes. This is just a Node subclass, and does nothing. Hence, don't modify it.
    
    """
    keyword = 'end'
    takes_subs = ()

class IfNode(Tag):
    """
    IfNode
    ======

    A tag node which is used for validation conditions.
    
    """
    keyword = 'if'
    takes_subs = ('elif', 'else')
    def __init__(self, value, body):
        """
        IfNode.__init__
        ===============

        __init__(self, value, body) -> IfNode instance
        Instantiate the ``if`` node. Overridden because we want the value raw and parsable rather than preparsed into several arguments.
        
        """
        self.subtags = []
        self.value, self.body = value, body

    def render(self, context):
        """
        IfNode.render
        =============

        render(self, context) -> string
        Render the node using the sent context. This particular node will validate the condition and, based on the output, return either the sent body or the value of an ``else`` subtag if applicable.
        
        """
        try:
            else_body = self.get_sub('else')
        except NoSuchSubtag:
            else_body = NodeList()

        cond = Conditional(context)
        if cond.evaluate(self.value):
            return self.body.render(context)
        else:
            for condition, body in self.get_subs('elif'):
                value = condition.split(' ', 1)[1]
                if cond.evaluate(value):
                    return body.render(context)
            else:
                if self.get_sub('else'):
                    return self.get_sub('else').render(context)

                return ''

class ForNode(Tag):
    """
    ForNode
    =======

    A tag node for looping through sequences, a la Python's ``for *x* in *y*``.
    
    """
    keyword = 'for'
    def render(self, context):
        """
        ForNode.render
        ==============

        render(self, context) -> string
        Render the node using the sent context. The value previously sent will be parsed and the appropriate sequence looped through.

        Some context arguments will be provided during the loop. These are:

        length
          The length of the sequence passed.
        pos
          The current position in the loop, starting from 1.
        zpos
          ``pos`` - 1; the position starting from zero.  

        """
        single, multiple = self.value[0], ArgumentParser.parse_one(context, self.value[2])
        out = []

        if 'loop' in context:
            context['loop'] = {'parent': context['loop']}
        else:
            context['loop'] = {}

        context['loop'].update({'length': len(multiple)})
        for n, item in enumerate(multiple):
            context[single] = item
            context['loop'].update({'pos': n + 1, 'zpos': n})
            out.append(self.body.render(context))

        if 'parent' in context['loop']:
            context['loop'] = context['loop']['parent']
        else:
            del context['loop']

        return ''.join(out)

tags = {
    'print': PrintNode,
    'end': EndNode,
    'for': ForNode,
    'if': IfNode,
}
